#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#include <stdio.h>
#include <fcntl.h>


#define BUF_SIZE 1024


///struct sembuf
//{
///	short sem_num;   /* semaphore number: 0 = first */
///	short sem_op;    /* semaphore operation */
///	short sem_flg;   /* operation flags */
//}

union semun
{
	int val;                /* value for SETVAL */
	struct semid_ds *buf;   /* buffer for IPC_STAT & IPC_SET */
	ushort *array;          /* array for GETALL & SETALL */
	struct seminfo *__buf;  /* buffer for IPC_INFO */
	void *__pad;
};

int main( int argc, char **argv)
{
	if ( argc != 2)
	{
		printf("Wrong arguments\n");
		exit(1);
	}
	
	int fd_in = open( (char*)argv[1], O_RDONLY);
	if ( fd_in == -1)
	{
		printf("Can't open file: %s",(char*)argv[1]);
		exit(0);
	}
	
	
	int fd =  open( ".for_keys", O_CREAT | 0644);
	if ( fd != -1)
		close(fd);
///////////////////////////////////////////////
struct sembuf sem_undo_inc, sem_undo_dec;
		sem_undo_inc.sem_num = 0;
		sem_undo_inc.sem_flg = SEM_UNDO;
		sem_undo_inc.sem_op  = 1;
		
		sem_undo_dec.sem_num = 0;
		sem_undo_dec.sem_flg = SEM_UNDO;
		sem_undo_dec.sem_op  = -1;
		
struct sembuf sem_inc2, sem_dec2;
		
		sem_inc2.sem_num = 0;
		sem_inc2.sem_flg = NULL;
		sem_inc2.sem_op  = 2;
		
		sem_dec2.sem_num = 0;
		sem_dec2.sem_flg = NULL;
		sem_dec2.sem_op  = -2;
		
struct sembuf sem_inc, sem_dec;
		
		sem_inc.sem_num = 0;
		sem_inc.sem_flg = NULL;
		sem_inc.sem_op  = 1;
		
		sem_dec.sem_num = 0;
		sem_dec.sem_flg = NULL;
		sem_dec.sem_op  = -1;
///////////////////////////////////////////////
	key_t key_sem_common_empty  = ftok( ".for_keys", 1);
	key_t key_sem_common_full   = ftok( ".for_keys", 2);
	key_t key_sem_common_mutex  = ftok( ".for_keys", 3);
	
	key_t key_shm_common = ftok( ".for_keys", 10);
///////////
	int sem_id_common_mutex = semget( key_sem_common_mutex,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_mutex == -1){
		sem_id_common_mutex = semget( key_sem_common_mutex ,1, NULL);
	}
	else
	{
		semop( sem_id_common_mutex, &sem_inc2, 1);
	}
	
	int sem_id_common_empty  = semget( key_sem_common_empty ,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_empty == -1){
		sem_id_common_empty  = semget( key_sem_common_empty ,1, NULL);
	}
	else
	{
		semop( sem_id_common_empty, &sem_inc2, 1);
	}
	
	int sem_id_common_full = semget( key_sem_common_full,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_full == -1){
		sem_id_common_full = semget( key_sem_common_full,1, 0644);
	}
	
	int sem_id_buffer_read  = semget( IPC_PRIVATE   ,1,IPC_CREAT | 0644);
	int sem_id_buffer_write = semget( IPC_PRIVATE   ,1,IPC_CREAT | 0644);
///////////////
	int shm_id_common = shmget( key_shm_common, 4*sizeof( int), IPC_CREAT | IPC_EXCL | 0644);
	if ( shm_id_common == -1){
		shm_id_common = shmget( key_shm_common, 4*sizeof( int), 0644);
	}
	int shm_id_buffer = shmget( IPC_PRIVATE   , BUF_SIZE      , IPC_CREAT | 0644);
	int shm_id_count  = shmget( IPC_PRIVATE   , sizeof(int)   , IPC_CREAT | 0644);
	
///////////////
	int  * ptr_shm_common = (int* ) shmat( shm_id_common, NULL, NULL);
	char * ptr_shm_buffer = (char*) shmat( shm_id_buffer, NULL, NULL);
	int  * ptr_shm_count  = (int* ) shmat( shm_id_count , NULL, NULL);
////////////////////////////////////////////////
	semop ( sem_id_common_empty, &sem_dec2,1);
		semop ( sem_id_common_mutex, &sem_dec2,1);
			
			ptr_shm_common[ 0] = shm_id_buffer;
			ptr_shm_common[ 1] = shm_id_count;
			ptr_shm_common[ 2] = sem_id_buffer_read;
			ptr_shm_common[ 3] = sem_id_buffer_write;

		semop ( sem_id_common_mutex, &sem_inc2,1);
	semop ( sem_id_common_full, &sem_inc2,1);
////////////////////////////////////////////////
	printf("sem_read %d \t sem_write %d\n",sem_id_buffer_read,sem_id_buffer_write);
	
	
	int read_count;
	do
	{
		semop( sem_id_buffer_write, &sem_dec,1);

			read_count = read( fd_in, ptr_shm_buffer, BUF_SIZE);
			if ( read_count == -1)
			{
				perror("In read error");
			}
			else
			{
				*ptr_shm_count = read_count;		
			}
			
		semop( sem_id_buffer_read , &sem_inc, 1);
	}
	while( read_count);
	
	
	shmdt( ptr_shm_common);
	shmdt( ptr_shm_buffer);
	shmdt( ptr_shm_count);
	
	shmctl( shm_id_buffer, IPC_RMID , NULL);
	shmctl( shm_id_count,  IPC_RMID , NULL);
	
//	semop( sem_id_common_mutex, &sem_inc, 1);
//	semop( sem_id_common_empty, &sem_inc, 1);
}